/* Copyright ©, 2022, ECARX (Hubei) Technology Co., Ltd. All rights reserved.
 *
 * NOTICE: This file contains information which is confidential and proprietary to ECARX
 * (Hubei) Technology Co., Ltd. and/or other developers. No license to any intellectual
 * property rights contained within this file is either granted or implied except as may be
 * provided in an agreement with ECARX (Hubei) Technology Co., Ltd.
 *
 * Any unauthorized copying or distribution of content from this file in whole or in part
 * is prohibited.
 */
#pragma once

#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include <unordered_map>

#include "vhal_core/Common.h"

namespace ecarx::vhal::core::property::storage {
class PropValueStore {
  public:
    using ReportPropertyValueFunction = std::function<void(const VehiclePropValue& value)>;

  private:
    struct CollectValue {
        VehiclePropValue firstValue;
        VehiclePropValue lastValue;
        CommitMode mode;
    };

    enum class State {
        INIT = 0,
        WAITE_COMMIT = 1,
        COMMITTING = 2,
    };

    using PropertyMap = std::map<RecordId, VehiclePropValue>;
    using PropertyCollectMap = std::map<RecordId, CollectValue>;

  public:
    PropValueStore() : mState(State::INIT), mCommitCounter(0) {}
    void readReportPropValues(const std::function<void(const VehiclePropValue&)>&);

    bool writeValue(const VehiclePropValue& propValue,CommitMode mode);

    void removeValue(const VehiclePropValue& propValue);

    std::vector<VehiclePropValue> readAllValues() const;
    std::unique_ptr<VehiclePropValue> readValueOrNull(const VehiclePropValue& request) const;
    std::unique_ptr<VehiclePropValue> readValueOrNull(int32_t prop, int32_t area = 0) const;

    void beginTransaction();
    void endTransaction();
    void waiteCommit();
    void commitCompleted();
    void deactivate();

  private:
    RecordId getRecordIdLocked(const VehiclePropValue& valuePrototype) const;
    const VehiclePropValue* getValueOrNullLocked(const RecordId& recId) const;

  private:
    using MuxGuard = std::lock_guard<std::mutex>;
    mutable std::mutex mLock;
    //上传数据锁
    mutable std::mutex mReportLock;
    //提交锁
    mutable std::mutex mCommitLock;
    //事务锁
    mutable std::mutex mTransactionLock;

    PropertyMap mPropertyValues;  // Sorted map of RecordId : VehiclePropValue.
    PropertyCollectMap mReportValues;
    std::atomic<State> mState;
    std::atomic_uint16_t mCommitCounter;
    std::condition_variable mReportCond;
    std::condition_variable mCommitCond;
    bool mIsActive = true;
};

class PropValueConsumer {
  private:
    enum class State {
        INIT = 0,
        RUNNING = 1,
        STOP_REQUESTED = 2,
        STOPPED = 3,
    };

  public:
    PropValueConsumer() : mState(State::INIT) {}

    PropValueConsumer(const PropValueConsumer&) = delete;
    PropValueConsumer& operator=(const PropValueConsumer&) = delete;

    using OnBatchReceivedFunc = std::function<void(const VehiclePropValue& vec)>;

    void run(property::storage::PropValueStore* store,

             const OnBatchReceivedFunc& func) {
        mStore = store;

        mWorkerThread = std::thread(&PropValueConsumer::runInternal, this, func);
    }

    void requestStop() { mState = State::STOP_REQUESTED; }

    void waitStopped() {
        if (mWorkerThread.joinable()) {
            mWorkerThread.join();
        }
    }

  private:
    void runInternal(const OnBatchReceivedFunc& onBatchReceived) {
        if (mState.exchange(State::RUNNING) == State::INIT) {
            while (State::RUNNING == mState) {
                mStore->waiteCommit();
                if (State::STOP_REQUESTED == mState) break;
                mStore->readReportPropValues(onBatchReceived);
                mStore->commitCompleted();
            }
        }

        mState = State::STOPPED;
    }

  private:
    std::thread mWorkerThread;

    std::atomic<State> mState;
    property::storage::PropValueStore* mStore;
};

}  // namespace ecarx::vhal::core::property::storage
